package com.kylin.common.core.filter;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.kylin.common.core.annotation.NoApiInterceptor;
import com.kylin.common.core.annotation.aspect.RsaAspect;
import com.kylin.common.core.beans.ResultBean;
import com.kylin.common.core.security.SecurityService;
import com.kylin.common.core.util.ServletUtils;
import com.kylin.common.core.util.SpringUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerExecutionChain;
import org.springframework.web.servlet.HandlerMapping;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
import org.springframework.web.util.ContentCachingRequestWrapper;
import org.springframework.web.util.WebUtils;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.Method;
import java.net.InetAddress;
import java.nio.charset.StandardCharsets;

/**
 * 接口日志过滤器
 * @author wuhao
 * @version 1.0 - 2023/4/23
 */
//@WebFilter(filterName = "apiLogFilter", urlPatterns = "/**")
//在springsecurity中添加了，所以这个地方就不要注解了
public class ApiLogFilter implements Filter {

    @Override
    public void destroy() {

    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        if("application/json".equals(request.getContentType())) {
            long startTime = System.currentTimeMillis();
            ResponseWrapper responseWrapper = new ResponseWrapper((HttpServletResponse) response);
            HttpServletRequest httpRequest = (HttpServletRequest) request;

            // 检查请求方法上是否存在NoApiInterceptor注解
            HandlerExecutionChain executionChain = getHandlerExecutionChain(httpRequest);
            if (executionChain != null && hasNoApiInterceptorAnnotation(executionChain)) {
                chain.doFilter(request, response);
                return;
            }

            String requestParams = "";
            if (httpRequest.getMethod().equalsIgnoreCase("POST")) {
                ContentCachingRequestWrapper wrappedRequest = new ContentCachingRequestWrapper(httpRequest);
                chain.doFilter(wrappedRequest, responseWrapper);

                requestParams = getRequestBody(wrappedRequest);
                // do something with the request body
            } else {
                requestParams = JSONObject.toJSONString(WebUtils.getParametersStartingWith(httpRequest, ""));
                chain.doFilter(httpRequest, responseWrapper);
            }

            long endTime = System.currentTimeMillis();

            // 获取response返回的内容并重新写入response
            String result = responseWrapper.getResponseData(response.getCharacterEncoding());
            response.getOutputStream().write(result.getBytes());

            ResultBean resultBean = null;
            try {
                // 空的时候，可能是回调类接口地址，对方需要的不是标准的resultbean格式
                if (!StringUtils.hasText(result)) {
                    resultBean = new ResultBean(0, "成功，非标准resultbean格式");
                } else {
                    resultBean = JSON.parseObject(result, ResultBean.class);
                }
            } catch (Exception e) {
                resultBean = new ResultBean(-2, "解析异常,具体接口返回数据可参考result");
            }

            RsaAspect rsaAspect = new RsaAspect();
            InetAddress addr = InetAddress.getLocalHost();
            SecurityService securityService = SpringUtils.getBean(SecurityService.class);

            // 可能返回的不是json字符串，需要做处理
            JSONObject resultJson = new JSONObject();
            try {
                resultJson = JSONObject.parseObject(result);
            } catch (Exception e) {
                resultJson.put("data", result);
            }

            ApiLog apiLog = ApiLog.builder()
                    .code(resultBean.getCode())
                    .msg(resultBean.getMsg())
                    .times(endTime - startTime)
                    .params(JSON.parseObject(requestParams))
                    .decryptParams(JSON.parseObject(ApiLogThreadLocal.getContext().getDecryptParams()))
                    .result(resultJson)
                    .url(httpRequest.getRequestURI())
                    .method(httpRequest.getMethod())
                    .exception(ApiLogThreadLocal.getContext().getException())
                    .ip(addr.getHostAddress())
                    .userIp(ServletUtils.getIpAddr((HttpServletRequest) request))
                    .userId(securityService == null ? null : (Long) securityService.getUserId())
                    .build();

            ApiLogHandler apiLogHandler = SpringUtils.getBean(ApiLogHandler.class);
            apiLogHandler.handle(apiLog);

            ApiLogThreadLocal.remove();
        }else {
            chain.doFilter(request, response);
        }

    }

    @Override
    public void init(FilterConfig fConfig) throws ServletException {

    }

    // 辅助方法，用于获取请求的HandlerExecutionChain
    private HandlerExecutionChain getHandlerExecutionChain(HttpServletRequest request) {
        HandlerMapping handlerMapping = SpringUtils.getBean(RequestMappingHandlerMapping.class);
        if (handlerMapping != null) {
            try {
                return handlerMapping.getHandler(request);
            } catch (Exception e) {
                // 处理异常情况
            }
        }
        return null;
    }

    // 辅助方法，检查方法上是否存在NoApiInterceptor注解
    private boolean hasNoApiInterceptorAnnotation(HandlerExecutionChain executionChain) {
        Object handler = executionChain.getHandler();
        if (handler instanceof HandlerMethod) {
            Method method = ((HandlerMethod) handler).getMethod();
            return method.isAnnotationPresent(NoApiInterceptor.class);
        }
        return false;
    }

    private String getRequestBody(ContentCachingRequestWrapper wrappedRequest) {
        byte[] buffer = wrappedRequest.getContentAsByteArray();
        if (buffer.length > 0) {
            return new String(buffer, StandardCharsets.UTF_8);
        } else {
            return "";
        }
    }

}
